#include "GLHeaders.hpp"
#include <cmath>
#include "Color.hpp"

//////////////////////
//    Constructor   //
//////////////////////

Color::Color(void){
	r = (GLubyte)0x00;
	g = (GLubyte)0x00;
	b = (GLubyte)0x00;
	a = (GLubyte)0x00;
}

Color::Color(const GLfloat fa){
  GLfloat tmpf;
  GLubyte tmp;
  tmpf = (fa < 0.0f)? 0.0f : fa;   //0.0f..tmp...INF
  tmpf = (tmpf > 1.0f)? 1.0f : tmpf; //0.0f..tmp...1.0f
  tmp = (GLubyte)roundf(tmpf * 255);

  r = tmp;
  g = tmp;
  b = tmp;
  a = 0xff;
}

Color::Color(const GLint ba){
  GLint tmpi;
  tmpi = (ba < 0)? 0 : ba; //0..tmpi...INF
  tmpi = (tmpi > 255)? 255 : tmpi;  //0..tmpi...255

	r = (GLubyte)tmpi;
	g = (GLubyte)tmpi;
	b = (GLubyte)tmpi;
	a = 0xff;
}

Color::Color(const GLfloat fr, const GLfloat fg, const GLfloat fb, const GLfloat fa){
 GLfloat tmpr, tmpg, tmpb, tmpa;
  tmpr = (fr < 0.0f)? 0.0f : fr;     //0.0f..tmp...INF
  tmpr = (tmpr > 1.0f)? 1.0f : tmpr; //0.0f..tmp...1.0f
  tmpg = (fg < 0.0f)? 0.0f : fg; 
  tmpg = (tmpg > 1.0f)? 1.0f : tmpg;
  tmpb = (fb < 0.0f)? 0.0f : fb;
  tmpb = (tmpb > 1.0f)? 1.0f : tmpb;
  tmpa = (fa < 0.0f)? 0.0f : fa;
  tmpa = (tmpa > 1.0f)? 1.0f : tmpa;

  r = (GLubyte)roundf(tmpr * 255);
	g = (GLubyte)roundf(tmpg * 255);
	b = (GLubyte)roundf(tmpb * 255);
	a = (GLubyte)roundf(tmpa * 255);
}

Color::Color(const GLint br, const GLint bg, const GLint bb, const GLint ba){
  GLint tmpir, tmpig, tmpib, tmpia;
  tmpir = (br < 0)? 0 : br;           //0..tmp...INF
  tmpir = (tmpir > 255)? 255 : tmpir; //0..tmp...255
  tmpig = (bg < 0)? 0 : bg; 
  tmpig = (tmpig > 255)? 255 : tmpig;
  tmpib = (bb < 0)? 0 : bb;
  tmpib = (tmpib > 255)? 255 : tmpib;
  tmpia = (ba < 0)? 0 : ba;
  tmpia = (tmpia > 255)? 255 : tmpia;

  r = (GLubyte)tmpir;
	g = (GLubyte)tmpig;
	b = (GLubyte)tmpib;
	a = (GLubyte)tmpia;
}

Color::Color(const Color &color){
	r = color.r;
	g = color.g;
	b = color.b;
	a = color.a;
}

//////////////////////
//    Destructor    //
//////////////////////

Color::~Color(void){}

////////////////////////////////
//    Arithmetic operations   //
////////////////////////////////

Color Color::operator + () const{
	return Color(r, g, b,	a);
}

Color Color::operator - () const{
	return Color(0xff-r, 0xff-g, 0xff-b, a);
}

Color Color::operator + (const Color &rhs) const{
  GLubyte tmpR, tmpG, tmpB, tmpA;

  if(0xff < r+rhs.r)
    tmpR = 0xff;
  else
    tmpR = r+rhs.r;
  
  if(0xff < g+rhs.g)
    tmpG = 0xff; 
  else
    tmpG = g+rhs.g;
  
  if(0xff < b+rhs.b)
    tmpB = 0xff;
  else
    tmpB = b+rhs.b;

  tmpA = ComposeA(a, rhs.a);

  return Color(tmpR, tmpG, tmpB, tmpA);
}

Color Color::operator + (const GLfloat value) const{
  GLfloat tmpf;
  GLubyte tmpR, tmpG, tmpB, tmp;
  
  tmpf = (value < -1.0f) ? -1.0f : value; // 0.0f...tmpf...INF
  tmpf = (tmpf > 1.0f) ? 1.0f : tmpf;   // 0.0f...tmpf...1.0f
  tmp = (GLubyte)roundf(tmpf * 255);    // 0x00...tmp...0xff

  if(0xff < r+tmp)
    tmpR = 0xff;
  else if(0x00 > r+tmp)
    tmpR = 0x00;
  else
    tmpR = r+tmp;
  
  if(0xff < g+tmp)
    tmpG = 0xff;
  else if(0x00 > g+tmp)
    tmpG = 0x00;
  else
    tmpG = g+tmp;
  
  if(0xff < b+tmp)
    tmpB = 0xff;
  else if(0x00 > b+tmp)
    tmpB = 0x00;
  else
    tmpB = b+tmp;
 
  return Color(tmpR, tmpG, tmpB, a);
}

Color Color::operator + (const GLint value) const{
  GLint tmpi;
  GLubyte tmpR, tmpG, tmpB, tmp;
  tmpi = (value < 0x00) ? 0x00 : value;
  tmpi = (tmpi > 0xff) ? 0xff : tmpi;
  tmp = tmpi;

  if(0xff < r+tmp)
    tmpR = 0xff;
  else
    tmpR = r+tmp;
  if(0xff < g+tmp)
    tmpG = 0xff; 
  else
    tmpG = g+tmp;
  if(0xff < b+tmp)
    tmpB = 0xff;
  else
    tmpB = b+tmp;

  return Color(tmpR, tmpG, tmpB, a);
}


Color Color::operator - (const Color &rhs) const{
  GLubyte tmpR, tmpG, tmpB, tmpA;

  if(0x00 > r-rhs.r)
    tmpR = 0x00;
  else
    tmpR = r-rhs.r;
  
  if(0x00 > g-rhs.g)
    tmpG = 0x00; 
  else
    tmpG = g-rhs.g;
  
  if(0x00 > b-rhs.b)
    tmpB = 0x00;
  else
    tmpB = b-rhs.b;

  tmpA = ComposeA(a, rhs.a);

  return Color(tmpR, tmpG, tmpB, tmpA);
}

Color Color::operator - (const GLfloat value) const{
  GLfloat tmpf;
  GLubyte tmpR, tmpG, tmpB, tmp;
  
  tmpf = (value < -1.0f) ? -1.0f : value; // 0.0f...tmpf...INF
  tmpf = (tmpf > 1.0f) ? 1.0f : tmpf;   // 0.0f...tmpf...1.0f
  tmp = (GLubyte)roundf(tmpf * 255);    // 0x00...tmp...0xff

  if(0x00 > r-tmp)
    tmpR = 0x00;
  else if(0xff < r-tmp)
    tmpR = 0xff;
  else
    tmpR = r-tmp;

  if(0x00 > g-tmp)
    tmpG = 0x00;
  else if(0xff < g-tmp)
    tmpG = 0xff;
  else
    tmpG = g-tmp;

  if(0x00 > b-tmp)
    tmpB = 0x00;
  else if(0xff < b-tmp)
    tmpB = 0xff;
  else
    tmpB = b-tmp;
 
  return Color(tmpR, tmpG, tmpB, a);
}

Color Color::operator - (const GLint value) const{
  GLint tmpi;
  GLubyte tmpR, tmpG, tmpB, tmp;
  tmpi = (value < 0x00) ? 0x00 : value;
  tmpi = (tmpi > 0xff) ? 0xff : tmpi;
  tmp = tmpi;

  if(0x00 > r-tmp)
    tmpR = 0x00;
  else
    tmpR = r-tmp;

  if(0x00 > g-tmp)
    tmpG = 0x00; 
  else
    tmpG = g-tmp;

  if(0x00 > b-tmp)
    tmpB = 0x00;
  else
    tmpB = b-tmp;

  return Color(tmpR, tmpG, tmpB, a);
}

Color Color::operator * (const Color &rhs) const{
 GLubyte tmpR, tmpG, tmpB, tmpA;

  if(255.0f < roundf((float)r*rhs.r/255.0f))
    tmpR = 0xff;
  else
    tmpR = (GLubyte)roundf((float)r*rhs.r/255.0f);

  if(255.0f < roundf((float)g*rhs.g/255.0f))
    tmpG = 0xff; 
  else
    tmpG = (GLubyte)roundf((float)g*rhs.g/255.0f);

  if(255.0f < roundf((float)b*rhs.b/255.0f))
    tmpB = 0xff;
  else
    tmpB = (GLubyte)roundf((float)b*rhs.b/255.0f);

  tmpA = ComposeA(a, rhs.a);

  return Color(tmpR, tmpG, tmpB, tmpA);
}

Color Color::operator * (const GLfloat value) const{
 GLubyte tmpR, tmpG, tmpB;

  if(255.0f < roundf((float)r*value))
    tmpR = 0xff;
  else if(0.0f > roundf((float)r*value))
    tmpR = 0x00;
  else
    tmpR = (GLubyte)roundf((float)r*value);

  if(255.0f < roundf((float)g*value))
    tmpG = 0xff; 
  else if(0.0f > roundf((float)g*value))
    tmpG = 0x00;
  else
    tmpG = (GLubyte)roundf((float)g*value);

  if(255.0f < roundf((float)b*value))
    tmpB = 0xff;
  else if(0.0f > roundf((float)b*value))
    tmpB = 0x00;
  else
    tmpB = (GLubyte)roundf((float)b*value);
  
  return Color(tmpR, tmpG, tmpB, a);
}

Color Color::operator / (const Color &rhs) const{
 GLubyte tmpR, tmpG, tmpB, tmpA;

  if(0x00 == rhs.r)
    tmpR = 0xff;
  else
    tmpR = (GLubyte)roundf((float)r*255.0f/rhs.r);

  if(0x00 == rhs.g)
    tmpG = 0xff; 
   else
    tmpG = (GLubyte)roundf((float)g*255.0f/rhs.g);

  if(0x00 == rhs.b)
    tmpB = 0xff;
  else
    tmpB = (GLubyte)roundf((float)b*255.0f/rhs.b);
 
  tmpA = ComposeA(a, rhs.a);;

  return Color(tmpR, tmpG, tmpB, tmpA);
}

Color Color::operator / (const GLfloat value) const{
  GLubyte tmpR, tmpG, tmpB;

  if(0.0f == value || 255.0f < roundf((float)r/value))
    tmpR = 0xff;
  else if(0.0f > roundf((float)r/value))
    tmpR = 0x00;
  else
    tmpR = (GLubyte)roundf((float)r/value);

  if(0.0f == value || 255.0f < roundf((float)g/value))
    tmpG = 0xff; 
  else if(0.0f > roundf((float)g/value))
    tmpG = 0x00;
  else
    tmpG = (GLubyte)roundf((float)g/value);

  if(0.0f == value || 255.0f < roundf((float)b/value))
    tmpB = 0xff;
  else if(0.0f > roundf((float)b/value))
    tmpB = 0x00;
  else
    tmpB = (GLubyte)roundf((float)b/value);

  return Color(tmpR, tmpG, tmpB, a);
}

//////////////////////////
//    Friend Methods    //
//////////////////////////

Color operator + (const GLfloat value, const Color &rhs){
  GLfloat tmpf;
  GLubyte tmpR, tmpG, tmpB, tmp;
  
  tmpf = (value < -1.0f) ? -1.0f : value; // 0.0f...tmpf...INF
  tmpf = (tmpf > 1.0f) ? 1.0f : tmpf;   // 0.0f...tmpf...1.0f
  tmp = (GLubyte)roundf(tmpf * 255);    // 0x00...tmp...0xff

  if(0xff < (tmp+rhs.r))
    tmpR = 0xff;
  else
    tmpR = tmp+rhs.r;

  if(0xff < (tmp+rhs.g))
    tmpG = 0xff;
  else
    tmpG = tmp+rhs.g;
  
  if(0xff < (tmp+rhs.b))
    tmpB = 0xff;
  else
    tmpB = tmp+rhs.b;
  
  return Color(tmpR, tmpG, tmpB, rhs.a);
}

Color operator + (const GLint value, const Color &rhs){
  GLubyte tmpR, tmpG, tmpB;

  if(0xff < (value+rhs.r))
    tmpR = 0xff;
  else if(0x00 > (value+rhs.r))
    tmpR = 0x00;
  else
    tmpR = (GLubyte)(value+rhs.r);

  if(0xff < (value+rhs.g))
    tmpG = 0xff;
  else if(0x00 > (value+rhs.g))
    tmpG = 0x00;
  else
    tmpG = (GLubyte)(value+rhs.g);

  if(0xff < (value+rhs.b))
    tmpB = 0xff;
  else if(0x00 > (value+rhs.b))
    tmpB = 0x00;
  else
    tmpB = (GLubyte)(value+rhs.b);

  return Color(tmpR, tmpG, tmpB, rhs.a);
}

Color operator - (const GLfloat value, const Color &rhs){
  GLfloat tmpf;
  GLubyte tmpR, tmpG, tmpB, tmp;
  
  tmpf = (value < -1.0f) ? -1.0f : value; // 0.0f...tmpf...INF
  tmpf = (tmpf > 1.0f) ? 1.0f : tmpf;   // 0.0f...tmpf...1.0f
  tmp = (GLubyte)roundf(tmpf * 255);    // 0x00...tmp...0xff

  if(0x00 > (tmp-rhs.r))
    tmpR = 0x00;
  else if(0xff < (tmp-rhs.r))
    tmpR = 0xff;
  else
    tmpR = tmp-rhs.r;

  if(0x00 > (tmp-rhs.g))
    tmpG = 0x00;
  else if(0xff < (tmp-rhs.g))
    tmpG = 0xff;
  else
    tmpG = tmp-rhs.g;

  if(0x00 > (tmp-rhs.b))
    tmpB = 0x00;
  else if(0xff < (tmp-rhs.b))
    tmpB = 0xff;
  else
    tmpB = tmp-rhs.b;

  return Color(tmpR, tmpG, tmpB, rhs.a);
}

Color operator - (const GLint value, const Color &rhs){
  GLubyte tmpR, tmpG, tmpB;
  
  if(0xff < (value-rhs.r))
    tmpR = 0xff;
  else if(0x00 > (value-rhs.r))
    tmpR = 0x00;
  else
    tmpR = (GLubyte)(value-rhs.r);

  if(0xff < (value-rhs.g))
    tmpG = 0xff;
  else if(0x00 > (value-rhs.g))
    tmpG = 0x00;
  else
    tmpG = (GLubyte)(value-rhs.g);

  if(0xff < (value-rhs.b))
    tmpB = 0xff;
  else if(0x00 > (value-rhs.b))
    tmpB = 0x00;
  else
    tmpB = (GLubyte)(value-rhs.b);

  return Color(tmpR, tmpG, tmpB, rhs.a);
}

Color operator * (const GLfloat value, const Color &rhs){
  GLubyte tmpR, tmpG, tmpB;

  if(0xff < roundf(value*rhs.r))
    tmpR = 0xff;
  else if(0x00 > roundf(value*rhs.r))
    tmpR = 0x00;
  else
    tmpR = (GLubyte)roundf(value*rhs.r);

  if(0xff < roundf(value*rhs.g))
    tmpG = 0xff; 
  else if(0x00 > roundf(value*rhs.g))
    tmpG = 0x00;
  else
    tmpG = (GLubyte)roundf(value*rhs.g);

  if(0xff < roundf(value*rhs.b))
    tmpB = 0xff;
  else if(0x00 > roundf(value*rhs.b))
    tmpB = 0x00;
  else
    tmpB = (GLubyte)roundf(value*rhs.b);

  return Color(tmpR, tmpG, tmpB, rhs.a);
}

Color operator / (const GLfloat value, const Color &rhs){
  GLubyte tmpR, tmpG, tmpB;

  if(0x00 == rhs.r || 0xff < roundf(value/rhs.r))
    tmpR = 0xff;
  else if(0x00 > roundf(value/rhs.r))
    tmpR = 0x00;
  else
    tmpR = (GLubyte)roundf(value/rhs.r);

  if(0x00 == rhs.g || 0xff < roundf(value/rhs.g))
    tmpG = 0xff; 
  else if(0x00 > roundf(value/rhs.g))
    tmpG = 0x00;
  else
    tmpG = (GLubyte)roundf(value/rhs.g);

  if(0x00 == rhs.b || 0xff < roundf(value/rhs.b))
    tmpB = 0xff;
  else if(0x00 > roundf(value/rhs.b))
    tmpB = 0x00;
  else
    tmpB = (GLubyte)roundf(value/rhs.b);

  return Color(tmpR, tmpG, tmpB, rhs.a);
}

//////////////////////////////
//    Arithmetic updates    //
//////////////////////////////

Color& Color::operator = (const Color &rhs){
  this->r = rhs.r;
	this->g = rhs.g;
	this->b = rhs.b;
	this->a = rhs.a;

	return *this;
}

Color& Color::operator = (const GLfloat value){
  GLfloat tmpf;
  GLubyte tmp;
  tmpf = (value < 0.0f)? 0.0f : value;   //0.0f..tmp...INF
  tmpf = (tmpf > 1.0f)? 1.0f : tmpf; //0.0f..tmp...1.0f
  tmp = (GLubyte)roundf(tmpf * 255);

  this->r = tmp;
  this->g = tmp;
  this->b = tmp;
  this->a = 0xff;

	return *this;
}

Color& Color::operator = (const GLint value){
  GLint tmpi;
  GLubyte tmp;
  tmpi = (value < 0)? 0 : value; //0..tmpi...INF
  tmpi = (tmpi > 255)? 255 : tmpi;  //0..tmpi...255
  tmp = (GLubyte)tmpi;

  this->r = tmp;
  this->g = tmp;
  this->b = tmp;
  this->a = 0xff;

	return *this;
}

Color& Color::operator += (const Color &rhs){
  *this = *this + rhs;

  return *this;
}

Color& Color::operator += (const GLfloat value){
 *this = *this + value;

	return *this;
}

Color& Color::operator += (const GLint value){
 *this = *this + value;

	return *this;
}

Color& Color::operator -= (const Color &rhs){
 *this = *this - rhs;

  return *this;
}

Color& Color::operator -= (const GLfloat value){
  *this = *this - value;

	return *this;
}

Color& Color::operator -= (const GLint value){
  *this = *this - value;

	return *this;
}

Color& Color::operator *= (const Color &rhs){
  *this = *this * rhs;

  return *this;
}

Color& Color::operator *= (const GLfloat value){
  *this = *this * value;

  return *this;
}
 
Color& Color::operator /= (const Color &rhs){
  *this = *this / rhs;

	return *this;
}

Color& Color::operator /= (const GLfloat value){
  *this = *this / value;

	return *this;
}

////////////////////////////
//    Useful functions    //
////////////////////////////

Color Color::Inverse(void){
  return Color(255-r, 255-g, 255-b, a);
}

Color Color::Add(const Color &fg, const Color &bg){
  GLubyte tmpR, tmpG, tmpB, tmpA;

  tmpA = ComposeA(fg.a, bg.a);
  tmpR = ComposeC(fg.a, fg.r, bg.a, bg.r, tmpA);
  tmpG = ComposeC(fg.a, fg.g, bg.a, bg.g, tmpA);
  tmpB = ComposeC(fg.a, fg.b, bg.a, bg.b, tmpA);

  return Color(tmpR, tmpG, tmpB, tmpA);
}

Color Color::Min(const Color &c0, const Color &c1){
  GLubyte tmpR, tmpG, tmpB, tmpA;

  tmpR = (c0.r < c1.r) ? c0.r : c1.r;
  tmpG = (c0.g < c1.g) ? c0.g : c1.g;
  tmpB = (c0.b < c1.b) ? c0.b : c1.b;
  tmpA = ComposeA(c0.a, c1.a);

  return Color(tmpR, tmpG, tmpB, tmpA);
}

Color Color::Max(const Color &c0, const Color &c1){
  GLubyte tmpR, tmpG, tmpB, tmpA;

  tmpR = (c0.r > c1.r) ? c0.r : c1.r;
  tmpG = (c0.g > c1.g) ? c0.g : c1.g;
  tmpB = (c0.b > c1.b) ? c0.b : c1.b;
  tmpA = ComposeA(c0.a, c1.a);

  return Color(tmpR, tmpG, tmpB, tmpA);
}

//////////////////////////
//    Special vector    //
//////////////////////////
const Color Color::NONE(   0x00, 0x00, 0x00, 0x00);
const Color Color::BLACK(  0x00, 0x00, 0x00, 0xff); 
const Color Color::BLUE(   0x00, 0x00, 0xff, 0xff); 
const Color Color::GREEN(  0x00, 0xff, 0x00, 0xff);
const Color Color::CYAN(   0x00, 0xff, 0xff, 0xff);
const Color Color::RED(    0xff, 0x00, 0x00, 0xff);
const Color Color::MAGENTA(0xff, 0x00, 0xff, 0xff);
const Color Color::YELLOW( 0xff, 0xff, 0x00, 0xff);
const Color Color::WHITE(  0xff, 0xff, 0xff, 0xff); 

